from sklearn.datasets import load_digits
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
import logging
from pinecone import Pinecone, ServerlessSpec
from tqdm import tqdm

# 设置日志输出级别
logging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(levelname)s - %(message)s')

# 初始化 Pinecone
pinecone = Pinecone(api_key="972121d6-7afd-4634-bc4a-3708ce15caaa")

# 创建索引
# 索引名称
index_name = "mnist-index"

# 获取现有索引列表
existing_indexes = pinecone.list_indexes()

# 检查索引是否存在，如果存在就删除
# 这个if是否需要，要看情况而定
# 比如有的时候，如果不想要重复删除在创建，这个if就可以不要
if any(index['name'] == index_name for index in existing_indexes):
    print(f"索引 '{index_name}' 已存在，正在删除...")
    pinecone.delete_index(index_name)
    print(f"索引 '{index_name}' 已成功删除。")
else:
    print(f"索引 '{index_name}' 不存在，将创建新索引。")

# 创建新索引
print(f"正在创建新索引 '{index_name}'...")
pinecone.create_index(
    name=index_name,
    dimension=64,  # MNIST 每个图像展平后是一个 64 维向量
    metric="euclidean",  # 使用欧氏距离
    spec=ServerlessSpec(
        cloud="aws",
        region="us-east-1"
    )
)
print(f"索引 '{index_name}' 创建成功。")

# 连接到索引
index = pinecone.Index(index_name)
print(f"已成功连接到索引 '{index_name}'。")


# 使用 load_digits 函数加载 MNIST 数据集
# n_class=10 表示加载全部 10 个数字类别(0-9)
digits = load_digits(n_class=10)

# 获取数据集中的特征数据
# X 是一个二维数组,每行代表一个样本,每个样本是一个 64 维的向量(8x8 像素展平)
X = digits.data

# 获取数据集中的标签
# y 是一个一维数组,包含每个样本对应的真实数字标签(0-9)
y = digits.target

# 初始化一个空列表,用于存储转换后的向量数据
vectors = []

# 遍历所有样本,将数据转换为 Pinecone 可接受的格式
for i in range(len(X)):
    # 使用样本的索引作为向量的唯一标识符
    vector_id = str(i)
    
    # 将 NumPy 数组转换为 Python 列表
    # Pinecone 要求输入数据为 Python 列表格式
    vector_values = X[i].tolist()
    
    # 创建元数据字典,包含该样本的真实标签
    # 将标签转换为整数类型,确保数据类型的一致性
    metadata = {"label": int(y[i])}
    
    # 将转换后的数据(ID、向量值、元数据)作为元组添加到 vectors 列表中
    vectors.append((vector_id, vector_values, metadata))

# 定义批处理大小,每批最多包含 1000 个向量
# 这是为了避免一次性向 Pinecone 发送过多数据,可能导致请求超时或失败
batch_size = 1000

# 使用步长为 batch_size 的 range 函数,实现分批处理
for i in tqdm(range(0, len(vectors), batch_size), desc='上传数据'):
    batch = vectors[i:i + batch_size]
    index.upsert(batch)

# 训练 KNN 分类器
# 这里使用 K=11, 即 KNN 分类器的 K 值
knn = KNeighborsClassifier(n_neighbors=11)

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 训练 KNN 分类器
knn.fit(X_train, y_train)

# 使用测试集对 KNN 分类器进行测试
for j in tqdm(range(len(X_test)), desc='测试模型'):   
    # 计算准确率
    accuracy = knn.score(X_test, y_test)
print(f"K=11时的准确率为 {accuracy:.2f}")

